package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.service.IAlertDataService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 报警数据Service业务层处理
 * 
 * @author zzyl
 * @date 2025-08-26
 */
@Service
public class AlertDataServiceImpl extends ServiceImpl<AlertDataMapper, AlertData> implements IAlertDataService
{
    @Autowired
    private AlertDataMapper alertDataMapper;
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 查询报警数据
     * 
     * @param id 报警数据主键
     * @return 报警数据
     */
    @Override
    public AlertData selectAlertDataById(Long id)
    {
        return alertDataMapper.selectById(id);
    }

    /**
     * 查询报警数据列表
     * 
     * @param alertData 报警数据
     * @return 报警数据
     */
    @Override
    public List<AlertData> selectAlertDataList(AlertData alertData)
    {
        return alertDataMapper.selectAlertDataList(alertData);
    }

    /**
     * 新增报警数据
     * 
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int insertAlertData(AlertData alertData)
    {
        return alertDataMapper.insert(alertData);
    }

    /**
     * 修改报警数据
     * 
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int updateAlertData(AlertData alertData)
    {
        return alertDataMapper.updateById(alertData);
    }

    /**
     * 批量删除报警数据
     * 
     * @param ids 需要删除的报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataByIds(Long[] ids)
    {
        return alertDataMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 过滤设备上报的数据是否满足报警规则
     */
    @Override
    public void alertFilter() {
        //1.统计规则数量，如果没有直接结束
        Long count = alertRuleMapper.selectCount(new LambdaQueryWrapper<AlertRule>().eq(AlertRule::getStatus, 1));
        if (count <= 0){
            return;
        }
        //2.从redis中获取所有设备上报的数据
        List<List<DeviceData>> values = redisTemplate.opsForHash().values(Constants.IOT_DEVICE_LAST_DATA);
        //3.没有数据  直接结束
        if (CollUtil.isEmpty( values)){
            return;
        }
        //4.开始遍历数据  处理每一条数据是否慢走报警规则
        List<DeviceData> alDeviceData = new ArrayList<>();
        values.forEach(alDeviceData::addAll);
        alDeviceData.forEach(this::dateAlertFilter);


    }

    private void dateAlertFilter(DeviceData date) {
        //1.如果当前上报的数据超过了一分钟  无效数据 直接结束
        if (date.getAlarmTime().plusMinutes(1).isBefore(LocalDateTime.now())) {
            return;
        }
        //2.获取产品下 设备的对应的 五模型规则
        List<AlertRule> alertRules = alertRuleMapper.selectList(new LambdaQueryWrapper<AlertRule>()
                .eq(AlertRule::getProductKey, date.getProductKey())
                .in(AlertRule::getIotId, -1,date.getIotId())
                .eq(AlertRule::getFunctionId, date.getFunctionId())
                .eq(AlertRule::getStatus, 1));
        //3.判断规则是否为空  为空 直接结束
        if (CollUtil.isEmpty(alertRules)){
            return;
        }
        //4.遍历每一条规则
        alertRules.forEach(rule -> dataIsMatchRule(rule,date));

    }

    /**
     * 判断数据是否满足报警规则
     * @param rule
     * @param date
     */
    private void dataIsMatchRule(AlertRule rule, DeviceData date) {
        String durationKey=Constants.ALERT_TRIGGER_COUNT_PREFIX+date.getProductKey()+"_"+date.getIotId()+"_"+date.getFunctionId()+"_"+rule.getId();
        String silentKey=Constants.ALERT_SILENT_PREFIX+date.getProductKey()+"_"+date.getIotId()+"_"+date.getFunctionId()+"_"+rule.getId();
        //1.判断是否在生效时间内 不在 直接结束
        if (!alarmTimeIsMatch(rule, date)) {
            return;
        }
        //2.检查上报数据是否达到阈值   未达到 删除报警数（持续周期）  直接结束
        if (!dataValueIsMatch(rule, date)) {
            redisTemplate.delete(durationKey);
            return;
        }
        //3.判断是否有沉默周期  有  直接结束


       String silenceCycle = (String) redisTemplate.opsForValue().get(silentKey);
        if (StringUtils.isNotEmpty(silenceCycle)) {
            return;
        }
        //4.查询redis中的报警数  如果没有  则设置成1  有则加1
        Integer count = (Integer) redisTemplate.opsForValue().get(durationKey);
        if (ObjUtil.isNull( count)){
            count=1;
        }else {
            count++;
        }
        //5.判断 报警数 是否达到 持续周期 未达到 将报警数重新更新回redis 直接结束
        if (ObjUtil.notEqual(count,rule.getDuration())){
            redisTemplate.opsForValue().set(durationKey,count);
            return;
        }
        //6.redis删除报警数 删除沉默周期
        redisTemplate.delete(durationKey);
        redisTemplate.opsForValue().set(silentKey,"1",rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
        //7.批量保存报警数据
        batchInsertAlertData(rule,date);

    }

    /**
     * 批量保存报警数据
     * @param date
     */
    private void batchInsertAlertData(AlertRule rule,DeviceData date) {
        //查询报警数据腰通知的用户列表
        List<Long> adminIds = sysUserRoleMapper.selectUserIdsByRoleName("超级管理员");
        List<Long> otherUserIds=null;
        if (rule.getAlertDataType()==0){
            if (date.getLocationType()==0){
                //老人
                otherUserIds = deviceMapper.selectNursingIdsByIotIdWithElder(date.getIotId());
            }else {
                otherUserIds = deviceMapper.selectNursingIdsByIotIdWithBed(date.getIotId());
                //床位
            }
        }else {
            otherUserIds = sysUserRoleMapper.selectUserIdsByRoleName("维修工");
        }
        Collection<Long> userIds = CollUtil.addAll(adminIds, otherUserIds);
        List<AlertData> alertDataList = new ArrayList<>();
        userIds.forEach(userId -> {
            AlertData alertData = BeanUtil.toBean(date, AlertData.class);
            alertData.setId(null);
            alertData.setAlertRuleId(rule.getId());
            String reason = StringUtils.format("报警原因:{},{},{},持续{}个周期", rule.getFunctionName(), rule.getOperator(), rule.getValue(), rule.getDuration());
            alertDataList.add(alertData);
            alertData.setAlertReason(reason);
            alertData.setType(rule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setUserId(userId);
            alertDataList.add(alertData);
        });
       this.saveBatch(alertDataList);

       //通过websocket推送消息给用户
        webSocketNotify(alertDataList.get(0),rule,userIds);

    }

    /**
     * 通过websocket推送消息给用户
     * @param alertData
     * @param rule
     * @param userIds
     */
    private void webSocketNotify(AlertData alertData, AlertRule rule, Collection<Long> userIds) {
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(rule.getFunctionName());
        alertNotifyVo.setAlertDataType(rule.getAlertDataType());
        alertNotifyVo.setVoiceNotifyStatus(0);
        alertNotifyVo.setNotifyType(1);
        if (alertData.getLocationType()==1 &&alertData.getPhysicalLocationType()==3){
            alertNotifyVo.setIsAllConsumer(true);
        }else {
            alertNotifyVo.setIsAllConsumer(false);
        }
        webSocketServer.sendMessageToConsumer(alertNotifyVo,userIds);
    }

    /**
     * 检查上报数据是否达到阈值   未达到 删除报警数（持续周期）  直接结束
     * @param rule
     * @param date
     * @return
     */
    private boolean dataValueIsMatch(AlertRule rule, DeviceData date) {
        try {
            Object dataValue = date.getDataValue();
            Double value = Double.valueOf(dataValue.toString());
            Double ruleValue = rule.getValue();
            //0 相等   1：前面大 -1：后面大
            if (">=".equals(rule.getOperator())&&Double.compare(value,ruleValue)<0
             ||"<".equals(rule.getOperator())&&Double.compare(value,ruleValue)>=0
            ){

                return false;
            }
        } catch (Exception e) {
            throw new BaseException("非数值型数值没有报警规则");
        }
        return true;
    }

    /**
     * 判断是否在生效时间内  不在:false  在:true
     * @param rule
     * @param date
     * @return
     */
    private boolean alarmTimeIsMatch(AlertRule rule, DeviceData date) {
        String[] split = rule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(split[0]);
        LocalTime end = LocalTime.parse(split[1]);
        LocalTime alertTime = date.getAlarmTime().toLocalTime();
        if (alertTime.isBefore( start) || alertTime.isAfter(end)){
            return false;
        }
        return true;
    }

    /**
     * 删除报警数据信息
     * 
     * @param id 报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataById(Long id)
    {
        return alertDataMapper.deleteById(id);
    }
}
